#include "vector_dinamico.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

/* ******************************************************************
 *                        PRUEBAS UNITARIAS							*
 * *****************************************************************/

/* Función auxiliar para imprimir si estuvo OK o no. */
void print_test(char* name, bool result) {
    printf("%s: %s\n", name, result? "OK" : "ERROR");
}

/* Prueba que las primitivas del vector funcionen correctamente. */
void prueba_vector_cero () {
	printf ("\nPRUEBAS DE VECTOR CON TAMAÑO CERO\n\n");
	vector_dinamico_t* v1;
	int *valor1 = malloc (sizeof(int));
	int *valor2 = malloc (sizeof(int));
	if ((!valor1) || (!valor2)) return;
	*valor1 = 5;
	*valor2 = 10;
	
	/* Pruebo la creación de un vector de tamaño 0 */
	v1 = vector_crear(0,NULL);
	print_test("Prueba vector crear v1 con tamaño 0", v1 != NULL);
	print_test("Prueba vector obtener cantidad v1 es 0", vector_cantidad(v1) == 0);
	print_test("Prueba vector obtener tamaño v1 es 0", vector_tamanio(v1) == 0);
	
	/* Pruebo que guardar en un vector de tamaño 0 devuelve false */
	print_test("Prueba vector guardar valor1 en v1[0] es false", !vector_guardar(v1, 0, valor1));
	print_test("Prueba vector obtener cantidad v1 es 0", vector_cantidad(v1) == 0);
	print_test("Prueba vector guardar valor2 en v1[1] es false", !vector_guardar(v1, 1, valor2));
	print_test("Prueba vector obtener cantidad v1 es 0", vector_cantidad(v1) == 0);
	
	/* Pruebo que borrar en un vector de tamaño 0 devuelve false */
	print_test("Prueba vector borrar en v1[0] es false", !vector_borrar(v1, 0));
	print_test("Prueba vector obtener cantidad v1 es 0", vector_cantidad(v1) == 0);
	print_test("Prueba vector borrar en v1[1] es false", !vector_borrar(v1, 1));
	print_test("Prueba vector obtener cantidad v1 es 0", vector_cantidad(v1) == 0);
	
	/* Pruebo que obtener valores de un vector de tamaño 0 devuelve NULL */
	print_test("Prueba vector obtener v1[0] es NULL", !vector_obtener(v1, 0));
	print_test("Prueba vector obtener v1[1] es NULL", !vector_obtener(v1, 1));
	print_test("Prueba vector obtener cantidad v1 es 0", vector_cantidad(v1) == 0);
	
	/* Destruyo el vector para liberar la memoria pedida */
	vector_destruir(v1);
	
	free(valor1);
	free(valor2);
}

void prueba_vector_con_tam () {
	printf ("\nPRUEBAS DE VECTOR CON TAMAÑO 5\n\n");
	vector_dinamico_t* v2;
	int *valor1 = malloc (sizeof(int));
	int *valor2 = malloc (sizeof(int));
	int *valor3 = malloc (sizeof(int));
	int *valor4 = malloc (sizeof(int));
	int *valor5 = malloc (sizeof(int));
	if ((!valor1) || (!valor2)) return;
	if ((!valor3) || (!valor4) || (!valor5)) return;
	*valor1 = 5; *valor2 = 10;
	*valor3 = 20; *valor4 = 30; *valor5 = 40;
	
	/* Pruebo la creación de un vector de tamaño 5 */
	v2 = vector_crear(5,free);
	print_test("Prueba vector crear v2 con tamaño 5", v2 != NULL);
	print_test("Prueba vector obtener tamaño v2 es 5", vector_tamanio(v2) == 5);

	/* Pruebo que guardar en un vector de tamaño 5 funciona para posiciones menores que 5 */
	print_test("Prueba vector guardar valor1 en v2[0] es true", vector_guardar(v2, 0, valor1));
	print_test("Prueba vector obtener cantidad v2 es 1", vector_cantidad(v2) == 1);
	print_test("Prueba vector guardar valor2 en v2[1] es true", vector_guardar(v2, 1, valor2));
	print_test("Prueba vector obtener cantidad v2 es 2", vector_cantidad(v2) == 2);
	
	/* Pruebo que guardar en un vector de tamaño 5 no funciona para posiciones mayores o iguales que 5 */
	print_test("Prueba vector guardar valor1 en v2[5] es false", !vector_guardar(v2, 5, valor3));
	print_test("Prueba vector obtener cantidad v2 es 1", vector_cantidad(v2) == 2);
	print_test("Prueba vector guardar valor2 en v2[6] es false", !vector_guardar(v2, 6, valor4));
	print_test("Prueba vector obtener cantidad v2 es 2", vector_cantidad(v2) == 2);

	/* Pruebo obtener los valores de un vector previamente guardados */
	print_test("Prueba vector obtener v2[0] es valor1", vector_obtener(v2, 0) == valor1);
	print_test("Prueba vector obtener cantidad v2 es 2", vector_cantidad(v2) == 2);
	print_test("Prueba vector obtener v2[1] es valor2", vector_obtener(v2, 1) == valor2);
	print_test("Prueba vector obtener cantidad v2 es 2", vector_cantidad(v2) == 2);

	/* Pruebo que tratar de obtener valores de posiciones inválidas devuelve NULL */
	print_test("Prueba vector obtener v2[5] es NULL", !vector_obtener(v2, 5));
	print_test("Prueba vector obtener cantidad v2 es 2", vector_cantidad(v2) == 2);
	print_test("Prueba vector obtener v2[6] es NULL", !vector_obtener(v2, 6));
	print_test("Prueba vector obtener cantidad v2 es 2", vector_cantidad(v2) == 2);

	/* Pruebo que borrar en un vector de tamaño 5 funciona para posiciones menores que 5 */
	print_test("Prueba vector borrar en v2[0] es valor1", vector_borrar(v2, 0) == valor1);
	print_test("Prueba vector obtener cantidad v2 es 1", vector_cantidad(v2) == 1);
	print_test("Prueba vector borrar en v2[0] es NULL", !vector_borrar(v2, 0));
	print_test("Prueba vector obtener cantidad v2 es 1", vector_cantidad(v2) == 1);
	print_test("Prueba vector borrar en v2[1] es valor2", vector_borrar(v2, 1) == valor2);
	print_test("Prueba vector obtener cantidad v2 es 0", vector_cantidad(v2) == 0);
	print_test("Prueba vector borrar en v2[1] es NULL", !vector_borrar(v2, 1));
	print_test("Prueba vector obtener cantidad v2 es 0", vector_cantidad(v2) == 0);

	/* Pruebo que guardar en un vector de tamaño 5 funciona para posiciones menores que 5 */
	print_test("Prueba vector guardar valor3 en v2[0] es true", vector_guardar(v2, 0, valor3));
	print_test("Prueba vector obtener v2[0] es valor3", vector_obtener(v2, 0) == valor3);
	print_test("Prueba vector obtener cantidad v2 es 1", vector_cantidad(v2) == 1);
	print_test("Prueba vector guardar valor4 en v2[1] es true", vector_guardar(v2, 1, valor4));
	print_test("Prueba vector obtener v2[1] es valor4", vector_obtener(v2, 1) == valor4);
	print_test("Prueba vector obtener cantidad v2 es 2", vector_cantidad(v2) == 2);

	/* Pruebo redimensionar un vector de 5 a 10 posiciones */
	print_test("Prueba vector redimensionar v2 a tamaño 10 es true", vector_redimensionar(v2, 10));
	print_test("Prueba vector obtener tamaño v2 es 10", vector_tamanio(v2) == 10);
	print_test("Prueba vector obtener cantidad v2 es 2", vector_cantidad(v2) == 2);

	/* Pruebo guardar y obtener un valor del vector redimensionado */
	print_test("Prueba vector guardar valor1 en v2[6] es true", vector_guardar(v2, 6, valor1));
	print_test("Prueba vector obtener v2[6] es valor1", vector_obtener(v2, 6) == valor1);
	print_test("Prueba vector obtener cantidad v2 es 3", vector_cantidad(v2) == 3);

	/* Pruebo redimensionar un vector de 10 a 3 posiciones */
	print_test("Prueba vector redimensionar v2 a tamaño 3 es false", !vector_redimensionar(v2,3));
	print_test("Prueba vector guardar valor2 en v2[7] es true", vector_guardar(v2,7,valor2));
	print_test("Prueba vector obtener v2[6] es valor1", vector_obtener(v2,6) == valor1);
	print_test("Prueba vector obtener v2[7] es valor2", vector_obtener(v2,7) == valor2);
	print_test("Prueba vector obtener cantidad v2 es 4", vector_cantidad(v2) == 4);

	/* Pruebo que los valores en las posiciones que quedan se conservan después de redimensionar */
	print_test("Prueba vector obtener v2[0] es valor3 después de redimensionar", vector_obtener(v2, 0) == valor3);
	print_test("Prueba vector obtener cantidad v2 es 4", vector_cantidad(v2) == 4);
	print_test("Prueba vector obtener v2[1] es valor4 después de redimensionar", vector_obtener(v2, 1) == valor4);
	print_test("Prueba vector obtener cantidad v2 es 4", vector_cantidad(v2) == 4);
	
	/* Pruebo que sobreescribir un valor */
	print_test("Prueba vector guardar valor5 en v2[0] (ocupada) es true", vector_guardar(v2, 0, valor5));
	print_test("Prueba vector obtener v2[0] es valor5 después de sobreescribir", vector_obtener(v2, 0) == valor5);
	print_test("Prueba vector obtener cantidad v2 es 4", vector_cantidad(v2) == 4);
	
	/* Pruebo si al mezclar se generan errores o pérdidas de memoria */
	print_test("Prueba vector mezclar", vector_mezclar (v2) );
	
	/* Destruyo el vector y sus elementos para liberar la memoria pedida */
	vector_destruir (v2);
}

void prueba_vector_mezclar (float cant) {
	printf ("\nPRUEBAS DE MEZCLA CON UN VECTOR DE %.00f ELEMENTOS\n\n",cant);
	
	/* Declaro variables */
	vector_dinamico_t* v1 = vector_crear(cant,free);
	int** v_ord = malloc (cant*sizeof(int*));
	if ( (!v_ord) || (!v1) ) return;
	
	/* Creo y guardo elementos */
	bool ok1 = true;
	for (size_t i = 0; i < cant; i++) {
		int* valor1 = malloc (sizeof(int));
		if (!valor1) break;
		v_ord[i] = valor1;
		ok1 = vector_guardar (v1,i,valor1);
		if (!ok1) break;
	}
	
	/* Si falló al guardar, destruyo los elementos creados y retorno */
	if (!ok1) {
		for (size_t i = 0; i < cant; i++) {
			if (v_ord[i]) free(v_ord[i]);
		}
		return;
	}
	
	/* Pruebo mezclar */
	vector_mezclar (v1);
	float q = 0;
	for (size_t i = 0; i < cant; i++) {
		if (v_ord[i] != (vector_obtener(v1,i))) q++;
	}
	
	print_test("Prueba vector mezclar", q != 0);
	float porcentaje = q / cant * 100;
	printf ("Porcentaje de mezcla: %.02f\n",porcentaje);
	
	/* Destruyo el vector y sus elementos para liberar la memoria pedida */
	vector_destruir(v1);
	free (v_ord);
}


void prueba_vector_volumen (size_t largo) {
	printf ("\nPRUEBAS DE VOLUMEN\n\n");
	vector_dinamico_t* v1 = vector_crear(5,free);
	bool ok1 = true;
	bool ok2 = true;
	bool ok3 = true;
	for (size_t i = 0; i < largo; i++) {
		int* valor1 = malloc (sizeof(int));
		if (!valor1) break;
		ok1 = vector_guardar (v1,i,valor1);
		if (i == (vector_tamanio(v1)-1)) {
			ok2 = vector_redimensionar(v1,3*i);
		}
		ok3 = (vector_obtener(v1, i) == valor1);
		if ((!ok1) || (!ok2) || (!ok3)) break;
	}
	
	print_test("Prueba vector guardar muchos elementos", ok1);
	print_test("Prueba vector redimensionar con muchos elementos", ok2);
	print_test("Prueba vector obtener valores de muchos elementos", ok3);
	print_test("Prueba vector obtener cantidad con muchos elementos", vector_cantidad(v1) == largo);
	print_test("Prueba vector mezclar muchos elementos", vector_mezclar (v1) );
	
	ok1 = true;
	for (size_t i = 0; i < (largo/2); i++) {
		int* valor2 = vector_borrar (v1,i);
		if (!valor2) break;
		free (valor2);
		ok1 = !vector_obtener(v1, i);
	}
	
	print_test("Prueba vector borrar muchos elementos", ok1);
	
	vector_destruir (v1); /* Debería destruir la 2da mitad de elementos */
}

/* ******************************************************************
 *                        PROGRAMA PRINCIPAL						*
 * *****************************************************************/

int main () {
	
	printf ("\nCOMIENZO DE PRUEBAS DE VECTOR DINAMICO.\n");
	
	/* Ejecuta todas las pruebas unitarias. */
	prueba_vector_cero ();
	prueba_vector_con_tam ();
	prueba_vector_mezclar(101);
	prueba_vector_volumen (10000);
	
	printf ("\nFIN DE PRUEBAS DE VECTOR DINAMICO.\n\n");
	return 0;
}
